<template>
  <div class="model-container">
    <div id="spaceModel" ref="spaceBim" />
    <div v-if="showInitial" class="model-operate">
      <el-tooltip effect="dark" placement="top-start">
        <div slot="content" style="font-size: 14px">初始视角</div>
        <img
          :src="`${qiankunStatic}/static/env/img/InitialPerspective.png`"
          style="width: 44px; height: 44px; cursor: pointer"
          alt=""
          @click="returnInitial"
        />
      </el-tooltip>
    </div>
  </div>
</template>

<script>
  import html2Canvas from 'html2canvas'
  import { qiankunStatic } from '@/utils/download'
  import RenderModel from '../js/renderModel'
  import * as THREE from 'three'
  // import { createTag2D } from '../js/label' // 模型对象
  import { createTag2DHtml } from '../js/tag' // 模型对象
  import { createTagHtml } from '../js/tagHtml' // html 对象
  export default {
    props: {
      isTrue: {
        type: Boolean,
        default: false,
      },

      currentData: {
        type: Object,
        default: () => {},
      },

      color: {
        type: String,
        default: '',
      },
      curView: {
        type: [String, Number],
        default: 'total',
      },
      defaultArea: {
        type: Array,
        default: () => [],
      },

      showInitial: {
        type: Boolean,
        default: false,
      },
    },
    data() {
      return {
        model: null,
        childModel: null,
        currentDetailPop: new Map(),
        drawPosition: {},
        curMovePoint: null,
        qiankunStatic,
        statusSprite: [],
        tagList: [],
        areaRoomList: [],
        areaCateList: [],
      }
    },
    watch: {},
    mounted() {
      //  为模型绑定点击事件
      const spaceModel = document.querySelector('#spaceModel')
      spaceModel.addEventListener('click', this.modelClick, false)
    },
    berforeDestroy() {
      const spaceModel = document.querySelector('#spaceModel')
      spaceModel.removeEventListener('click', this.modelClick)
      document.removeEventListener('mouseup', this.bindRayShotEvent)
    },
    methods: {
      // 模型加载
      initModel(url, fileType) {
        if (!url || !fileType) return
        this.childModel = null
        if (this.model) {
          this.model.clearAllModel()
          this.model.resetControl()
          this.model.setCameraPositon({
            x: 0,
            y: 32,
            z: 0,
          })
        } else {
          this.model = new RenderModel('#spaceModel')
          this.model.init()
        }

        this.model
          .setModel({
            filePath: url,
            fileType: fileType,
          })
          .then((res) => {
            this.childModel = res
            this.initAreaRoom()
            // this.initTag()
            // this.initStatusTag()
            this.initAnimation()
          })
      },
      // 多模型加载
      async initMutileModel(modelList, areaList) {
        this.childModel = null
        this.model = new RenderModel('#spaceModel')
        this.model.init()
        this.model.setCameraPositon({
          x: -21,
          y: 21,
          z: 28,
        })

        const len = modelList.length
        for (let i = 0; i < len; ++i) {
          const res = await this.model.setModel({
            filePath: modelList[i].modelUrl,
            fileType: modelList[i].modelFileType,
            position: {
              x: 0,
              y: 6 - i * 3,
              z: 0,
            },
          })
          this.childModel = res
          this.childModel.scale.set(1.2, 1.2, 1.2)

          for (let index = 0; index < areaList[i].length; index++) {
            const item = areaList[i][index]
            const [centerPoint, points] = this.getTagPoint(item)
            const statusPoints = this.calculateCenter([centerPoint, points[0]])
            this.drawStatusTag(statusPoints, item)
            this.drawAreaRoom(item)
            await this.drawTag(centerPoint, item)
          }

          // areaList[i].forEach((item) => {
          //   const [centerPoint, points] = this.getTagPoint(item)
          //   this.drawTag(centerPoint, item)
          //   const tagPoints = this.calculateCenter([centerPoint, points[0]])
          //   this.drawStatusTag(tagPoints, item)
          //   this.drawAreaRoom(item)
          // })
        }
        this.initAnimation()
        this.initModelScaleEvent()
        return new Promise((resolve) => resolve())
      },
      // 动画
      initAnimation() {
        this.model.initAnimation()
      },

      // 模型点击事件
      modelClick(e) {
        const raycaster = new THREE.Raycaster()
        const mouse = new THREE.Vector2()
        const boxDom = this.$refs.spaceBim
        const boxClinet = boxDom.getBoundingClientRect()
        // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
        mouse.x = ((e.clientX - boxClinet.left) / boxDom.offsetWidth) * 2 - 1
        mouse.y = -((e.clientY - boxClinet.top) / boxDom.offsetHeight) * 2 + 1
        raycaster.setFromCamera(mouse, this.model.camera)
        const intersects = raycaster.intersectObjects(this.model.scene.children)

        this.showAreaDetail(intersects)
      },

      // 模型切换
      changeView(index) {
        this.model.resetControl()
        if (index === 'total') {
          this.model.modelList.forEach((oneFloor) => {
            if (oneFloor.prePositionY === 0 || oneFloor.prePositionY) {
              oneFloor.position.y = oneFloor.prePositionY
              delete oneFloor.prePositionY
            }
            oneFloor.visible = true
          })
          this.model.setCameraPositon(new THREE.Vector3(-21, 21, 28))
          this.model.animationUpdate()
          return
        }
        this.model.modelList.forEach((oneFloor, i) => {
          if (i !== index) {
            if (oneFloor.prePositionY !== 0 && !oneFloor.prePositionY) {
              oneFloor.prePositionY = oneFloor.position.y
            }
            oneFloor.position.y = -20
            oneFloor.visible = false
          } else {
            if (oneFloor.prePositionY !== 0 && !oneFloor.prePositionY) {
              oneFloor.prePositionY = oneFloor.position.y
            }
            oneFloor.position.y = -1
            oneFloor.visible = true
            this.model.animationUpdate()
          }
        })
        this.model.setCameraPositon(new THREE.Vector3(0, 32, 0))
      },

      // 界面坐标转模型坐标
      get3DPosition(e) {
        const raycaster = new THREE.Raycaster()
        const mouse = new THREE.Vector2()
        const boxDom = this.$refs.spaceBim
        const boxClinet = boxDom.getBoundingClientRect()
        // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
        mouse.x = ((e.clientX - boxClinet.left) / boxDom.offsetWidth) * 2 - 1
        mouse.y = -((e.clientY - boxClinet.top) / boxDom.offsetHeight) * 2 + 1
        raycaster.setFromCamera(mouse, this.model.camera)
        const intersects = raycaster.intersectObjects(this.model.scene.children)
        if (intersects.length > 0) {
          return intersects[0].point
        }
      },
      /**
       * 框选区域end
       * --------------
       * 恢复初始视角
       */
      returnInitial() {
        this.model.resetControl()
        if (this.curView === 'total') {
          this.model.setCameraPositon(new THREE.Vector3(-21, 21, 28))
        } else {
          this.model.setCameraPositon(new THREE.Vector3(0, 32, 0))
        }
        this.model.animationUpdate()
      },

      showAreaDetail(intersects) {
        const withArea = intersects.filter(
          (item) => item.object.name === 'areaMesh'
        )

        withArea.forEach((item) => {
          if (item.object.areaId) {
            this.$emit('areaClick', { id: item.object.areaId })
          }
        })
      },

      calculateCenter(points) {
        // if (points.length === 0) {
        //   return null
        // }

        // let sumX = 0
        // let sumY = 0

        // for (let i = 0; i < points.length; i++) {
        //   sumX += points[i][0]
        //   sumY += points[i][1]
        // }

        // const centerX = sumX / points.length
        // const centerY = sumY / points.length

        // return [centerX, centerY]

        const center = new THREE.Vector3(0, 0, 0)
        for (let i = 0; i < points.length; i++) {
          center.add(points[i])
        }
        center.divideScalar(points.length)
        return center
      },

      getTagPoint(data, yAxis = 0) {
        let points = data.axis || []

        points = points.map((item) => {
          return Object.values(item)
        })

        points = points.map((item) => {
          return new THREE.Vector3(item[0], yAxis, item[1])
        })

        const centerPoint = this.calculateCenter(points)

        return [centerPoint, points]
      },

      async initTag() {
        // this.defaultArea.forEach((item) => {
        //   const [centerPoint] = this.getTagPoint(item)
        //   this.drawTag(centerPoint, item)
        // })

        for (let index = 0; index < this.defaultArea.length; index++) {
          const item = this.defaultArea[index]
          const [centerPoint] = this.getTagPoint(item)
          await this.drawTag(centerPoint, item)
        }
      },

      async drawTag(point, pointObj) {
        const currentPoint = pointObj || this.currentData
        // // // 用2d模型加载,不可以被遮挡
        const tag = createTag2DHtml(currentPoint, point)
        // tag.scale.set(0.02, 0.02, 1)
        // this.raycasterCollsionDetect(tag)
        this.tagList.push(tag)
        this.childModel.add(tag)
        return

        // 用精灵图的方式加载,可以被模型遮挡
        const element = createTagHtml(currentPoint, point)
        const canvas = await html2Canvas(element, {
          // width: 200, // canvas宽度
          // height: 200, // canvas高度
          // x: 0, // x坐标
          // y: 0, // y坐标
          allowTaint: true,
          backgroundColor: 'transparent', // 一定要添加背景颜色，否则出来的图片，背景全部都是透明的
          foreignObjectRendering: false, // 是否在浏览器支持的情况下使用ForeignObject渲染
          useCORS: true, // 是否尝试使用CORS从服务器加载图像
          async: true, // 是否异步解析和呈现元素
          // 以下字段必填
          scale: 3, // 处理模糊问题
          dpi: 600, // 处理模糊问题  300
        })
        const sprite = new THREE.Sprite(
          new THREE.SpriteMaterial({
            map: new THREE.CanvasTexture(canvas),
            transparent: true,
            // sizeAttenuation: true,
          })
        )
        sprite.scale.set(1.3, 1.2, 1.3)
        sprite.position.set(point.x, point.y, point.z)
        // sprite.position.y = point.y / 2; //
        sprite.name = 'Tag2DHtml' // 标签名
        this.childModel.add(sprite) // 添加至场景
        this.tagList.push(sprite)
        element.remove()
      },

      removeTag() {
        if (this.tagList.length) {
          this.childModel.remove(...this.tagList)
          this.tagList = []
        }
      },

      initStatusTag() {
        this.defaultArea.forEach((item) => {
          const [centerPoint, points] = this.getTagPoint(item) // 中心点
          const tapOpints = this.calculateCenter([centerPoint, points[0]]) // 偏离点
          this.drawStatusTag(tapOpints, item)
        })
      },

      drawStatusTag(point, pointObj) {
        const currentPoint = pointObj || this.currentData
        let png = ''
        if (currentPoint.operateStatus === 2) {
          png = 'point-warn'
        } else if (currentPoint.operateStatus === 3) {
          // 3
          png = 'point-error'
        }
        const texture = new THREE.TextureLoader().load(
          `${qiankunStatic}/static/env/point/${png}.png`
        )
        const material_text = new THREE.SpriteMaterial({ map: texture })
        const sprite = new THREE.Sprite(material_text)
        sprite.name = 'pointMarker'
        sprite.scale.set(0.5, 0.86, 1)
        sprite.position.set(point.x, point.y, point.z)
        sprite.pointInfo = currentPoint
        this.statusSprite.push(sprite)
        this.childModel.add(sprite)
      },

      removeStatusTag() {
        if (this.statusSprite.length) {
          this.childModel.remove(...this.statusSprite)
          this.statusSprite = []
        }
      },

      initAreaRoom() {
        this.defaultArea.forEach((item) => {
          this.drawAreaRoom(item)
        })
      },

      drawAreaRoom(item) {
        const points = item.axis
        const shape = new THREE.Shape()
        shape.moveTo(points[0].xx, points[0].yy) // 将初始点移动到第一个点位置
        const len = points.length
        for (let i = 1; i < len; i++) {
          shape.lineTo(points[i].xx, points[i].yy) // 绘制线
        }
        shape.autoClose = true // 自动闭合
        const planeMeshGeometry = new THREE.ShapeGeometry(shape)
        planeMeshGeometry.rotateX(Math.PI / 2)
        let color = ''
        let opacity = 1

        if (item.status === 1) {
          color = '#FF9900'
          opacity = 0.5
        } else if (item.status === 2) {
          color = '#08C55D'
          opacity = 0.3
        } else {
          color = '#356DEE'
          opacity = 0.5
        }
        const material = new THREE.MeshBasicMaterial({
          color: color,
          side: THREE.DoubleSide,
          transparent: true,
          opacity: opacity,
        })
        const planeMesh = new THREE.Mesh(planeMeshGeometry, material)
        planeMesh.name = 'areaMesh'
        planeMesh.areaId = item.id
        planeMesh.position.y = -0.8

        this.childModel.add(planeMesh)
        this.areaRoomList.push(planeMesh)
      },
      removeAreaRoom() {
        this.childModel.remove(...this.areaRoomList)
        this.areaRoomList = []
      },

      initAreaCate() {
        this.defaultArea.forEach((item) => {
          this.drawAreaCate(item)
        })
      },

      drawAreaCate(item) {
        const points = item.axis
        const shape = new THREE.Shape()
        shape.moveTo(points[0].xx, points[0].yy) // 将初始点移动到第一个点位置
        const len = points.length
        for (let i = 1; i < len; i++) {
          shape.lineTo(points[i].xx, points[i].yy) // 绘制线
        }
        shape.autoClose = true // 自动闭合
        const planeMeshGeometry = new THREE.ShapeGeometry(shape)
        planeMeshGeometry.rotateX(Math.PI / 2)
        let color = ''
        let opacity = 0

        if (item.commercialTypeName === '餐饮') {
          color = '#D969E0'
          opacity = 0.5
        } else if (item.commercialTypeName === '零售') {
          color = '#FADE61'
          opacity = 0.3
        } else if (item.commercialTypeName === '娱乐服务') {
          color = '#FE9EAE'
          opacity = 0.5
        }
        const material = new THREE.MeshBasicMaterial({
          color: color,
          side: THREE.DoubleSide,
          transparent: true,
          opacity: opacity,
        })
        const planeMesh = new THREE.Mesh(planeMeshGeometry, material)
        planeMesh.name = 'areaMesh'
        planeMesh.areaId = item.id
        planeMesh.position.y = -0.8

        this.childModel.add(planeMesh)
        this.areaCateList.push(planeMesh)
      },
      removeAreaCate() {
        this.childModel.remove(...this.areaCateList)
        this.areaCateList = []
      },

      raycasterCollsionDetect(wavesLabel) {
        // 创建一条射线 - 从相机发射到特定的世界坐标位置
        const model = this.model.scene?.children.filter((v) => {
          return v instanceof THREE.Group
        })
        if (model) {
          const position = wavesLabel.position.clone() // 射线的目标位置
          const raycaster = new THREE.Raycaster(
            this.model.camera.position,
            position.sub(this.model.camera.position).normalize()
          )
          raycaster.setFromCamera(position, this.model.camera)
          const intersects = raycaster.intersectObjects(model[0].children) // 使用射线和物体进行碰撞检测
          console.log(intersects.length)
          if (intersects.length === 0) {
            wavesLabel.element.classList.add('visible')
            wavesLabel.element.classList.remove('hidden')
          } else {
            wavesLabel.element.classList.add('hidden')
            wavesLabel.element.classList.remove('visible')
          }
        }
      },

      bindRayShotEvent() {
        if (this.tagList.length > 0) {
          this.tagList.forEach((c) => {
            this.raycasterCollsionDetect(c)
          })
        }
      },

      initModelScaleEvent() {
        return
        this.model.controls.addEventListener('change', () => {
          const scale = this.model.controls.target.distanceTo(
            this.model.camera.position
          )
          console.log(scale)
          this.tagList.forEach((item) => {
            // item.scale.set(scale / 10, scale / 10)
            // item.scale.set(1.3, 1.2, 1.3)
          })
        })
      },
    },
  }
</script>

<style lang="less" scoped>
  .model-container {
    // min-width: 640px;
    height: 100%;
    position: relative;
    #spaceModel {
      width: 100%;
      height: 100%;
    }
    .model-operate {
      position: absolute;
      top: 12px;
      right: 12px;
    }
  }
</style>
